/**
 * 
 */
package com.ms.service.module.baccarat.dm;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infinispan.util.concurrent.ConcurrentHashSet;

import com.ms.game.common.GameType;
import com.ms.game.gmtool.IDoormanProcesserToGMTool;
import com.ms.game.gmtool.message.SGmtoolServerShutdownCountdownNotify;
import com.ms.game.lobby.db.LobbyGameListWhileModel;
import com.ms.game.lobby.message.CLobbyHallMachinedataRequest;
import com.ms.game.slot.base.DMAsyncHsitoryManger;
import com.ms.game.slot.base.GainRateSystem;
import com.ms.game.slot.base.IDoorManSeatUpdateBroadcaster;
import com.ms.game.slot.base.ISlotDoormanProcesser;
import com.ms.game.slot.base.SlotDmSeatStatusBroadcastAsync;
import com.ms.game.slot.base.message.CSlotCommonDmSetRecvSeatUpdatePageRequest;
import com.ms.game.slot.base.message.SDMDoSeatRequest;
import com.ms.game.slot.base.message.SDMSeatStatChangeRequest;
import com.ms.game.slot.base.message.SDMVerifyExpriedTableUserRequest;
import com.ms.game.slot.base.message.SGmtoolCleanDoormanSeatStatusRequest;
import com.ms.game.slot.base.message.SGmtoolCleanGameSeatStatusRequest;
import com.ms.game.slot.base.message.STableGameAsynDoSeatDataRequest;
import com.ms.game.slot.base.message.SlotBasicDMMessageRequest;
import com.ms.game.slot.base.message.SlotBasicGameMessageRequest;
import com.ms.game.slot.base.message.SlotGameLeaveBackRequest;
import com.ms.game.slot.base.table.DMBasicTableUser;
import com.ms.game.slot.base.table.DMBasicTableUserInfo;
import com.ms.game.slot.base.table.DMBasicTableUserManager;
import com.ms.player.Player;
import com.ms.player.PlayerUserType;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.account.message.SCharacterLoginNotify;
import com.ms.service.module.account.message.SSessionCloseNotify;
import com.ms.service.module.analysis.AnalysisProcessor;
import com.ms.service.module.analysis.PlayerEnterSeatStatusType;
import com.ms.service.module.baccarat.BacrGameCore;
import com.ms.service.module.baccarat.BacrGameResult;
import com.ms.service.module.baccarat.BacrTableOption;
import com.ms.service.module.baccarat.BacrUserInfo;
import com.ms.service.module.baccarat.bin.BacrMachineSettingUtil;
import com.ms.service.module.baccarat.bin.BacrTableSettingInfo;
import com.ms.service.module.baccarat.db.BacrMachineDataModel;
import com.ms.service.module.baccarat.message.BacrSeatUpdateInfo;
import com.ms.service.module.baccarat.message.SBacrDoSeatResponse;
import com.ms.service.module.baccarat.message.SBacrDoormanAsyncHistoryData;
import com.ms.service.module.baccarat.message.SBacrDoormanTableGameResultUpdate;
import com.ms.service.module.baccarat.message.SBacrTableUpdateResponse;
import com.ms.service.module.baccarat.message.SBarcHallListResponse;
import com.ms.service.module.broadcastlistener.LoginLogoutListener;
import com.ms.service.module.datacenterlite.message.SDCLiteNotifySeatStatChangeRequest;
import com.ms.service.module.processer.BasiceServiceProcesser;
import com.xpec.c4.game.datamanager.agent.PlayerData;
import com.xpec.c4.game.datamanager.agent.PlayerDataAgent;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;

/**
 * @author TW15010
 *
 */
public class BacrDmProcessor extends BasiceServiceProcesser implements ISlotDoormanProcesser,
		IDoormanProcesserToGMTool, IDoorManSeatUpdateBroadcaster {

	@SuppressWarnings("unused")
	private static final Log log = LogFactory.getLog(BacrDmProcessor.class);

	/**
	 * 牌桌列表管理器
	 */
	private DMBasicTableUserManager<BacrUserInfo, BacrTableOption> seatinfo = new DMBasicTableUserManager<BacrUserInfo, BacrTableOption>();

	/**
	 * 簡易牌桌資訊 map<br>
	 * key: table id<br>
	 * value: 簡易牌桌資訊
	 */
	private HashMap<String, BacrDmTableInfo> dmTableInfoMap = new HashMap<String, BacrDmTableInfo>();

	/**
	 * 允許進入 GM 桌的 player id 白名單<br>
	 * key: player id
	 */
	private HashSet<GUID> playerIdWhiteList = new HashSet<GUID>();

	/**
	 * 存取資料管理器 {@link #seatUpdateInfoManager} 使用的館別字串
	 */
	public static final String HALLTYPE_FOR_SEAT_UPDATE_MANAGER = "BACR_NO_HALL";

	/**
	 * 存取資料管理器 {@link #seatUpdateInfoManager} 使用的頁數
	 */
	public static final int PAGE_FOR_SEAT_UPDATE_MANAGER = 1;

	/**
	 * 要更新給 client 的資料管理器
	 */
	private DMAsyncHsitoryManger<Object, BacrSeatUpdateInfo> seatUpdateInfoManager = new DMAsyncHsitoryManger<Object, BacrSeatUpdateInfo>(
			this);

	@Override
	public void setDMAsyncHsitoryMangerOwnModule(BasicServiceModule ownModule) {
		this.seatUpdateInfoManager.setOwnModule(ownModule);
	}

	/**
	 * tableId 轉 machineId 的 map<br>
	 * tableId \ machineId
	 */
	private HashMap<String, Integer> hallTypeToMachineIdMap = null;

	public DMBasicTableUserManager<BacrUserInfo, BacrTableOption> getSeatinfo() {
		return seatinfo;
	}

	public void setSeatinfo(DMBasicTableUserManager<BacrUserInfo, BacrTableOption> seatinfo) {
		this.seatinfo = seatinfo;
	}

	/**
	 * 從牌桌 model 初始化牌桌資料
	 * 
	 * @param model
	 */
	public void initFromMachineModel(BacrMachineDataModel model) {
		if (model == null) {
			return;
		}

		String tableId = model.getHalltype();

		BacrDmTableInfo info = new BacrDmTableInfo();

		// 目前第幾鋪
		info.setHandCount(model.getCurHandCount());

		// 歷史記錄
		ArrayList<BacrGameResult> gameRecordList = new ArrayList<BacrGameResult>();
		BacrGameCore.gameRecordListModelStringToData(model.getGameRecordList(), gameRecordList);
		info.setGameRecordList(gameRecordList);

		dmTableInfoMap.put(tableId, info);
	}

	/**
	 * 玩家入座
	 */
	@Override
	public void revDMDoSeat(SDMDoSeatRequest request) {
		GUID sessionid = this.baseaction.getSessionID();
		GUID player_guid = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionid);
		String halltype = request.getHalltype();

		String playerName = PlayerCommonInfoManager.getInstance().getPlayerNameByGuid(player_guid);
		String platformId = PlayerCommonInfoManager.getInstance().getLoginPlatformBySessionId(sessionid);

		// 玩家出入座記錄
		AnalysisProcessor.sendPlayerEnterSeat(playerName, player_guid.toString(), platformId, GameType.BACR,
				halltype, GainRateSystem.EXCEPT_GAME_GAIN_RATE_ID, 0,
				PlayerEnterSeatStatusType.ENTER_SEAT_REQUEST, 0, false, false);

		SBacrDoSeatResponse response = new SBacrDoSeatResponse();

		// 進入準備關服狀態
		if (baseaction.getModule().isWaitToClose()) {
			response.setErrorCode(ErrorCode.GAME_SERVER_WAIT_TO_CLOSE);
			sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
			return;
		}

		BacrUserInfo user = null;
		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(halltype);

		if (table == null) {
			response.setErrorCode(ErrorCode.SEAT_IS_EMPTY);
			sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
			return;
		}

		// 是否達到上限
		/*
		 * if(table.getSize()+1 >= table.getOption().maxUser)
		 * {
		 * response.setErrorCode(ErrorCode.SEAT_IS_FULL);
		 * sendMessageToClient(sessionid,
		 * MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
		 * return;
		 * }
		 */

		// 是否所有桌中己有此玩家
		HashMap<String, DMBasicTableUserInfo<BacrUserInfo, BacrTableOption>> allTable = seatinfo.getTable();
		for (java.util.Map.Entry<String, DMBasicTableUserInfo<BacrUserInfo, BacrTableOption>> entry : allTable
				.entrySet()) {
			DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> tt = entry.getValue();
			user = (BacrUserInfo) tt.getUser(player_guid.toString());
			if (user != null) {
				response.setErrorCode(ErrorCode.SEAT_HAS_PEOPLE);
				sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
				return;
			}
		}

		// 設定此玩家驗證中
		user = new BacrUserInfo();
		user.setGuid(player_guid.toString());
		user.setHalltype(halltype);
		user.setVerify(true);

		// 放入桌
		table.putUser(user);

		PlayerDataAgent agent = (PlayerDataAgent) ((BacrDmModule) baseaction.getModule())
				.getDCAgent(PlayerDataAgent.class);
		try {
			onGetPlayerData(agent, player_guid, "doSeatPlayerDataBack", sessionid, user, table.getOption());
		} catch (ClassNotFoundException e) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + " fail.", e);
			}

			user.setVerify(false);
			response.setErrorCode(ErrorCode.GAME_ERROR_UNKNOW);
			sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
			return;
		}
	}

	/**
	 * player common 讀取玩家資料回呼
	 */
	@Override
	public void doSeatPlayerDataBack(PlayerData playerData, Object[] userObject) {
		Player player = playerData.getPlayer();
		String guid = player.getGuid().toString();
		Object[] param = (Object[]) userObject[1];
		GUID sessionid = (GUID) param[0];
		BacrUserInfo seat = (BacrUserInfo) param[1];
		BacrTableOption option = (BacrTableOption) param[2];

		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(seat.getHalltype());
		BacrTableSettingInfo tableSetting = BacrMachineSettingUtil.getTableSetting(seat.getHalltype());

		// 是 GM 桌
		if (tableSetting.isGm()) {
			// 是玩家
			if (player.getUserType() == PlayerUserType.PLAYER) {
				// 不在白名單中
				if (!playerIdWhiteList.contains(player.getGuid())) {
					// 不給進入
					MessageAction.sendFailResultClientMessage(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK,
							SBacrDoSeatResponse.class, ErrorCode.BACR_PLAYER_IS_NOT_IN_WHITE_LIST);
					table.removeUser(guid);
					return;
				}
			}
		}

		// 體驗桌
		if (option.isTrial) {
			// 使用正式幣 && 不是機器人
			if (!player.isUseTrialCoin() && player.getUserType() != PlayerUserType.ROBORT) {
				// 不給進
				MessageAction.sendFailResultClientMessage(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK,
						SBacrDoSeatResponse.class, ErrorCode.BARC_NO_QUALIFIC);
				table.removeUser(guid);
				return;
			}
		} else { // 正式桌
			// 使用體驗幣
			if (player.isUseTrialCoin()) {
				// 不給進
				MessageAction.sendFailResultClientMessage(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK,
						SBacrDoSeatResponse.class, ErrorCode.BARC_NO_QUALIFIC);
				table.removeUser(guid);
				return;
			}
		}

		// 禁止寄送禮物期間不能進入百家樂
		if (player.isDisableSendGift()) {
			MessageAction.sendFailResultClientMessage(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK,
					SBacrDoSeatResponse.class, ErrorCode.BACR_DISABLE_SEND_GIFT_CAN_NOT_ENTER);
			table.removeUser(guid);
			return;
		}

		doSeatComplete(sessionid, seat, player);
	}

	/**
	 * 完成入座流程
	 * 
	 * @param sessionid
	 * @param halltype
	 * @param seat
	 * @param player
	 */
	public void doSeatComplete(GUID sessionid, BacrUserInfo user, Player player) {
		String guid = player.getGuid().toString();
		String nickname = player.getNameWithNumber();
		boolean isplayer = (player.getUserType() == PlayerUserType.PLAYER) ? true : false;
		boolean isgender = player.isGender();
		String halltype = user.getHalltype();
		SBacrDoSeatResponse response = new SBacrDoSeatResponse();

		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(halltype);
		if (table == null) {
			response.setErrorCode(ErrorCode.GAME_ERROR_UNKNOW);
			sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
			return;
		}

		// 設定
		user.setGuid(guid);
		user.setNickName(nickname);
		user.setUsertype(player.getUserType());
		user.setVerify(false);

		// find targetid
		String targetid = getTargetIDByTable(ModuleName.GROUP_BACR, halltype);

		// 同步至game server做驗證
		STableGameAsynDoSeatDataRequest request = new STableGameAsynDoSeatDataRequest();
		request.setGamename(GameType.BACR.toString());
		request.setHalltype(user.getHalltype());
		request.setPlayerGUID(player.getGuid());
		request.setExpriedtime(0);
		request.setUser(user);
		sendGroupMessage(MessageTypes.S_SLOT_ASYNC_DOSEATDATA_BYTABLE, request, targetid,
				this.baseaction.getModuleID());

		// 傳送給client
		response.setErrorCode(ErrorCode.SUCCESS);
		response.setTargeModuleID(targetid);
		response.setHalltype(halltype);
		sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);

		// 廣播通知client
		onBrocastSeatStatChange(halltype);

		// 強制停止傳送座位狀態更新
		if (player.getUserType() == PlayerUserType.PLAYER) {
			seatUpdateInfoManager.removeSession(sessionid);
			LoginLogoutListener
					.removeLogOutSessionListener(this.baseaction.getModule(), baseaction.getSessionID());
		}

		// 將自己加入收聽登入消息的module
		LoginLogoutListener.addLogInPlayerListener(this.baseaction.getModule(), player.getGuid());

	}

	/**
	 * 同步桌資訊至CLIENT
	 * 
	 * @param halltype
	 */
	public void onBrocastSeatStatChange(String halltype) {
		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(halltype);

		BacrSeatUpdateInfo updateInfo = new BacrSeatUpdateInfo();
		updateInfo.setNowUser(table.getSize());
		updateInfo.setHalltype(halltype);

		int machineId = hallTypeToMachineIdMap.get(halltype);

		seatUpdateInfoManager.updateSeatStatusAsyncInfo(HALLTYPE_FOR_SEAT_UPDATE_MANAGER,
				PAGE_FOR_SEAT_UPDATE_MANAGER, GainRateSystem.EXCEPT_GAME_GAIN_RATE_ID, machineId, updateInfo);
	}

	/**
	 * 廣播牌桌勝負資訊更新 to client
	 * 
	 * @param tableId
	 * @param gameResult
	 * @param clearHistory
	 *            是否清除歷史記錄
	 */
	private void broadcastTableGameResultUpdate(String tableId, BacrGameResult gameResult, boolean clearHistory) {
		SBacrDoormanTableGameResultUpdate msg = new SBacrDoormanTableGameResultUpdate();
		msg.setTableId(tableId);
		msg.setResult(gameResult);
		msg.setClearHistory(clearHistory);

		ConcurrentHashSet<GUID> sessionList = seatUpdateInfoManager.getSessionList(
				HALLTYPE_FOR_SEAT_UPDATE_MANAGER, GainRateSystem.EXCEPT_GAME_GAIN_RATE_ID,
				PAGE_FOR_SEAT_UPDATE_MANAGER);

		MessageAction.sendMessage(sessionList, MessageTypes.S_BACR_DOORMAN_TABLE_GAME_RESULT_UPDATE, msg);
	}

	@Override
	public void revDMSeatStatchange(SDMSeatStatChangeRequest request) {
		GUID sessionid = request.getSessionid();
		String halltype = request.getHalltype();
		Player player = request.getPlayer();
		String guid = player.getGuid().toString();
		String srcID = request.getSrcModuleID();
		SlotGameLeaveBackRequest response = new SlotGameLeaveBackRequest();

		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(halltype);
		if (table == null) {
			// 回覆game server
			response.setErrorCode(ErrorCode.SEAT_NOT_EXIST);
			response.setHalltype(halltype);
			response.setPlayer(player);
			response.setSessionid(sessionid);
			sendGroupMessage(MessageTypes.S_SLOT_LEAVEBACK, response, srcID, this.baseaction.getModuleID());
			return;
		}

		// 移除玩家資料
		table.removeUser(guid);

		// 回覆給game leave complete
		response.setErrorCode(ErrorCode.SUCCESS);
		response.setHalltype(halltype);
		response.setPlayer(player);
		response.setSessionid(sessionid);
		sendGroupMessage(MessageTypes.S_SLOT_LEAVEBACK, response, srcID, this.baseaction.getModuleID());

		// 廣播通知client
		onBrocastSeatStatChange(halltype);

		// 從收聽登入消息的module移除自己
		LoginLogoutListener.removeLogInPlayerListener(this.baseaction.getModule(), player.getGuid());
	}

	@Override
	public void revLobbyHallMachineList(CLobbyHallMachinedataRequest request) {
		String sessionid = request.getSessionId();
		GUID sessionID = GUID.parseUUID(sessionid);
		GUID player_guid = PlayerCommonInfoManager.getInstance().getPlayerGuidBySession(sessionID);

		PlayerDataAgent agent = (PlayerDataAgent) ((BacrDmModule) baseaction.getModule())
				.getDCAgent(PlayerDataAgent.class);
		try {
			onGetPlayerData(agent, player_guid, "doLobbyHallListPlayerDataBack", sessionID);
		} catch (ClassNotFoundException e) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + " fail.", e);
			}

			SBarcHallListResponse response = new SBarcHallListResponse();
			response.setErrorCode(ErrorCode.GAME_ERROR_UNKNOW);
			sendMessageToClient(sessionID, MessageTypes.S_BACR_DOORMAN_DOSEAT_BACK, response);
			return;
		}
	}

	/**
	 * 讀取桌列表
	 * 
	 * @param playerData
	 * @param userObject
	 */
	public void doLobbyHallListPlayerDataBack(PlayerData playerData, Object[] userObject) {
		Player player = playerData.getPlayer();
		boolean isTrial = player.isUseTrialCoin();
		Object[] param = (Object[]) userObject[1];
		GUID sessionid = (GUID) param[0];

		ArrayList<BacrTableOption> tables = new ArrayList<BacrTableOption>();
		HashMap<String, Integer> tableUserCount = new HashMap<String, Integer>();
		HashMap<String, ArrayList<BacrGameResult>> gameResultMap = new HashMap<String, ArrayList<BacrGameResult>>();
		HashMap<String, Integer> minBetValueMap = new HashMap<String, Integer>();

		// 掃一遍所有的桌
		HashMap<String, DMBasicTableUserInfo<BacrUserInfo, BacrTableOption>> allTable = seatinfo.getTable();
		for (java.util.Map.Entry<String, DMBasicTableUserInfo<BacrUserInfo, BacrTableOption>> entry : allTable
				.entrySet()) {
			// 正式玩家看不到體驗桌
			if (entry.getValue().getOption().isTrial && !isTrial) {
				continue;
			}

			String tableId = entry.getKey();
			BacrTableSettingInfo tableSetting = BacrMachineSettingUtil.getTableSetting(tableId);

			// 如果是 GM 桌
			if (tableSetting.isGm()) {
				// 是玩家
				if (player.getUserType() == PlayerUserType.PLAYER) {
					// 不在白名單中就看不見
					if (!playerIdWhiteList.contains(player.getGuid())) {
						continue;
					}
				}
			}

			tables.add(entry.getValue().getOption());
			tableUserCount.put(tableId, entry.getValue().getSize());

			// 牌桌的勝負歷史列表
			ArrayList<BacrGameResult> resultList = new ArrayList<BacrGameResult>();

			if (dmTableInfoMap.containsKey(tableId)) {
				BacrDmTableInfo tableInfo = dmTableInfoMap.get(tableId);
				resultList.addAll(tableInfo.getGameRecordList());
			}

			gameResultMap.put(tableId, resultList);

			// 最小押注額
			minBetValueMap.put(tableId, tableSetting.getMinimizeBetValueInOnePart());
		}

		SBarcHallListResponse response = new SBarcHallListResponse();
		response.setErrorCode(ErrorCode.SUCCESS);
		response.setTables(tables);
		response.setTableUserCount(tableUserCount);
		response.setGameResultMap(gameResultMap);
		response.setMinBetValueMap(minBetValueMap);
		sendMessageToClient(sessionid, MessageTypes.S_BACR_DOORMAN_TABLE_LIST, response);
	}

	@Override
	public void revSeatVerifyTimeOutNotify(SlotBasicDMMessageRequest request) {
		SDMVerifyExpriedTableUserRequest torequest = (SDMVerifyExpriedTableUserRequest) request;

		ArrayList<DMBasicTableUser> expried_data = torequest.getUserdata();

		for (DMBasicTableUser expriedUser : expried_data) {
			DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(expriedUser
					.getHalltype());

			table.removeUser(expriedUser.getGuid());

			// 廣播通知client
			onBrocastSeatStatChange(expriedUser.getHalltype());
		}
	}

	@Override
	public int revGmtoolCleanDoorManSeatStatus(SGmtoolCleanDoormanSeatStatusRequest request) {
		String guid = request.getKeepDataPlayerId();
		String halltype = request.getHalltype();

		DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(halltype);
		if (table == null) {
			return 0;
		}

		table.removeUser(guid);

		String targetid = getTargetIDByTable(ModuleName.GROUP_BACR, halltype);

		// 通知game server 踢人
		SGmtoolCleanGameSeatStatusRequest req = new SGmtoolCleanGameSeatStatusRequest();
		req.setHalltype(halltype);
		req.setPlayerId(GUID.parseUUID(guid));
		sendGroupMessage(MessageTypes.S_GMTOOL_CLEAN_GAME_SEAT_STATUS, req, targetid,
				this.baseaction.getModuleID());
		return ErrorCode.SUCCESS;
	}

	/**
	 * game 更新 dm 上的牌桌資訊
	 * 
	 * @param updateInfo
	 */
	public void revAysncHistoryData(SlotBasicDMMessageRequest request) {
		SBacrDoormanAsyncHistoryData updateInfo = (SBacrDoormanAsyncHistoryData) request;

		String tableId = updateInfo.getTableId();

		// 取出對應的牌桌資訊
		BacrDmTableInfo tableInfo = null;
		if (dmTableInfoMap.containsKey(tableId)) {
			// 有就拿出來
			tableInfo = dmTableInfoMap.get(tableId);
		} else {
			// 沒有就生一個
			tableInfo = new BacrDmTableInfo();
			dmTableInfoMap.put(tableId, tableInfo);
		}

		// 目前第幾鋪
		tableInfo.setHandCount(updateInfo.getHandCount());

		// 如果是第一鋪牌的結果就清掉歷史記錄
		if (updateInfo.isResetResultList()) {
			tableInfo.getGameRecordList().clear();
		}

		if (updateInfo.getResult() != null) {
			tableInfo.getGameRecordList().add(updateInfo.getResult());
		}

		// 廣播更新勝負結果給 client
		broadcastTableGameResultUpdate(tableId, updateInfo.getResult(), updateInfo.isResetResultList());
	}

	/**
	 * 從 db 載入白名單後觸發
	 * 
	 * @param whiteList
	 */
	public void onLoadWhiteLis(ArrayList<LobbyGameListWhileModel> whiteList) {
		// 清空
		playerIdWhiteList.clear();

		// 沒資料就直接結束
		if (GameUtility.getArrayListSize(whiteList) == 0) {
			return;
		}

		// 加到名單中
		for (LobbyGameListWhileModel model : whiteList) {
			// 設為禁止就不算
			if (model.isDeny()) {
				continue;
			}

			// 過期
			if (model.getExpired() < TimeSys.currentTimeMills()) {
				continue;
			}

			// 沒有填資料
			if (!GameUtility.isHaveString(model.getPlayerid())) {
				continue;
			}

			GUID playerId = GUID.parseUUID(model.getPlayerid());
			playerIdWhiteList.add(playerId);
		}
	}

	public void onPlayerLoginGameNotify(SCharacterLoginNotify notify) {
		if (notify.getPlayerId() == null) {
			return;
		}

		// 取出玩家所在的桌們, 要是失控時可能會有一個玩家在好幾桌的情況
		ArrayList<BacrUserInfo> userList = seatinfo.getAllUserInfo(notify.getPlayerId().toString());
		if (GameUtility.getArrayListSize(userList) == 0) {
			return;
		}

		// 處理有玩家的每一桌
		for (BacrUserInfo user : userList) {
			if (user == null) {
				continue;
			}

			String hallType = user.getHalltype();
			String playerId = user.getGuid();

			// 取出桌的資料
			DMBasicTableUserInfo<BacrUserInfo, BacrTableOption> table = seatinfo.getTable(hallType);
			if (table == null) {
				continue;
			}

			// 移除玩家
			table.removeUser(playerId);

			// 計算該桌所在的 module
			String targetid = getTargetIDByTable(ModuleName.GROUP_BACR, hallType);

			// 通知 game server 踢人
			SGmtoolCleanGameSeatStatusRequest req = new SGmtoolCleanGameSeatStatusRequest();
			req.setHalltype(hallType);
			req.setPlayerId(GUID.parseUUID(playerId));
			sendGroupMessage(MessageTypes.S_GMTOOL_CLEAN_GAME_SEAT_STATUS, req, targetid,
					this.baseaction.getModuleID());
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> Object getSeatUpdateMessageInstance(int messageType, Collection<T> dataList) {
		switch (messageType) {
		case MessageTypes.S_BACR_DOORMAN_TABLEINFO_UPDATE: {
			ArrayList<BacrSeatUpdateInfo> list = new ArrayList<BacrSeatUpdateInfo>();
			list.addAll((Collection<? extends BacrSeatUpdateInfo>) dataList);

			SBacrTableUpdateResponse msg = new SBacrTableUpdateResponse();
			msg.setSeatUpdateList(list);
			return msg;
		}
		}

		return null;
	}

	@Override
	public void onSlotDmSeatHistoryBroadcastTimer() {
		super.onSlotDmSeatHistoryBroadcastTimer();

		// 時間到廣播機台座位狀態
		if (seatUpdateInfoManager.isSendSeatStatusTime()) {
			if (!seatUpdateInfoManager.isSendSeatStatusWorking()) {
				seatUpdateInfoManager.setSendSeatStatusWorking(true);

				SlotDmSeatStatusBroadcastAsync<Object, BacrSeatUpdateInfo> asyncTask = new SlotDmSeatStatusBroadcastAsync<Object, BacrSeatUpdateInfo>();
				asyncTask.setSeatUpdateInfoManager(seatUpdateInfoManager);
				asyncTask.setSendSeatStatusMessageType(MessageTypes.S_BACR_DOORMAN_TABLEINFO_UPDATE);

				baseaction.getModule().addAsyncCommand(asyncTask);
			}
		}
	}

	@Override
	public void onRecvServerShutdownCountdownNotify(SGmtoolServerShutdownCountdownNotify notify) {
		this.seatUpdateInfoManager.onRecvServerShutdownCountdownNotify(notify);
	}

	@Override
	public void onRecvKickAllConnectionNotify() {
		this.seatUpdateInfoManager.onRecvKickAllConnectionNotify();
	}

	@Override
	public void revSetRecvSeatUpdatePageRequest(CSlotCommonDmSetRecvSeatUpdatePageRequest request) {
		// 檢查名稱
		if (!GameUtility.safeStringEquals(request.getGamename(), getGameType().name())) {
			return;
		}

		// 檢查頁數
		int page = request.getPage();
		if (page < 0 || page > 1) {
			return;
		}

		if (page == 0) {
			seatUpdateInfoManager.removeSession(baseaction.getSessionID());
			LoginLogoutListener
					.removeLogOutSessionListener(this.baseaction.getModule(), baseaction.getSessionID());
		} else {
			seatUpdateInfoManager.addSession(HALLTYPE_FOR_SEAT_UPDATE_MANAGER, baseaction.getSessionID(),
					GainRateSystem.EXCEPT_GAME_GAIN_RATE_ID, page, page);
		}
	}

	public void revDisConnect(SSessionCloseNotify request) {
		seatUpdateInfoManager.removeSession(request.getSessionId());
	}

	@Override
	public GameType getGameType() {
		return GameType.BACR;
	}

	// ----------------↓牌桌&新框架己不使用以下函式↓----------------
	
	@Override
	public void revSeatStatChange(SlotBasicGameMessageRequest request) throws ClassNotFoundException {
	}

	@Override
	public void revMachineDataList(SlotBasicDMMessageRequest request) {
		// TODO Auto-generated method stub
	}

	@Override
	public ArrayList<GUID> getHallUserNumber() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ErrorCode kickPlayerSeat(GUID player) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public ErrorCode cancelPlayerKeepData(GUID player) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getHallUserNumber(String halltype) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public boolean onKickPlayerOnMachine(String halltype, int machineid) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void onLobbyCancelOfflineDataBack(int errorCode, Object... userObject) {
		// TODO Auto-generated method stub
	}

	@Override
	public void revLobbyDebugCommand(SlotBasicDMMessageRequest request) throws ClassNotFoundException {
		// TODO Auto-generated method stub
	}

	@Override
	public void revDoSeatMachine(SlotBasicDMMessageRequest request) throws ClassNotFoundException {
		// TODO Auto-generated method stub
	}

	@Override
	public void revDoSeatCheckCurrentData(SlotBasicGameMessageRequest request) {
		// TODO Auto-generated method stub
	}

	@Override
	public void revDCLiteNotifyKeepDataEmpty(SDCLiteNotifySeatStatChangeRequest request) {
		// TODO Auto-generated method stub
	}

	@Override
	public void revSeatPlayerInfoRequest(SlotBasicDMMessageRequest request) {
		// TODO Auto-generated method stub
	}

	public void setHallTypeToMachineIdMap(HashMap<String, Integer> hallTypeToMachineIdMap) {
		this.hallTypeToMachineIdMap = hallTypeToMachineIdMap;
	}
}
